﻿/*******************************************************************************
 * Copyright © 2016-2017 立方技术工作室 版权所有
 * Author: 立方技术工作室
 * Description: 立方快速开发平台
 * Website：http://www.cubicfamily.com
*********************************************************************************/
using System;
using System.Collections.Generic;
using System.Data.Entity;
using System.Data.Entity.Validation;
using System.Linq;
using System.Linq.Expressions;
using System.Reflection;
using System.Text;
using System.Threading.Tasks;

using System.Data.Entity.Migrations;
using EntityFramework.Extensions;

using System.Data.Common;

namespace CubicFWNet.Data
{
    /// <summary>
    /// 扩展DbContext方法，简化数据存取层
    /// 刘立军
    /// 2017-1-25
    /// </summary>
    public static class ExtendDbContext
    {
        /// <summary>
        /// 插入一条数据
        /// </summary>
        /// <typeparam name="TEntity">实体对象类型</typeparam>
        /// <param name="dbcontext">数据库上下文</param>
        /// <param name="entity">实体对象</param>
        /// <returns></returns>
        public static int Insert<TEntity>(this DbContext dbcontext, TEntity entity) where TEntity : class
        {
            dbcontext.Entry<TEntity>(entity).State = EntityState.Added;
            return dbcontext.SaveChanges();
        }

        /// <summary>
        /// 批量插入
        /// </summary>
        /// <typeparam name="TEntity">实体对象类型</typeparam>
        /// <param name="dbcontext">数据库上下文</param>
        /// <param name="entitys">一组实体对象</param>
        /// <returns></returns>
        public static int Insert<TEntity>(this DbContext dbcontext, List<TEntity> entitys) where TEntity : class
        {
            foreach (var entity in entitys)
            {
                dbcontext.Entry<TEntity>(entity).State = EntityState.Added;
            }
            return dbcontext.SaveChanges();
        }

        /// <summary>
        /// 查询实体是否存在
        /// </summary>
        /// <typeparam name="TEntity">实体对象类型</typeparam>
        /// <param name="dbcontext">数据库上下文</param>
        /// <param name="exp">查询条件</param>
        /// <returns></returns>
        public static bool IsExist<TEntity>(this DbContext dbcontext, Expression<Func<TEntity, bool>> exp) where TEntity : class
        {
            return dbcontext.Set<TEntity>().Any(exp);
        }

        /// <summary>
        /// 根据过滤条件获取记录数
        /// </summary>
        /// <typeparam name="TEntity">实体对象类型</typeparam>
        /// <param name="dbcontext">数据库上下文</param>
        /// <param name="exp">过滤条件</param>
        /// <returns></returns>
        public static int GetCount<TEntity>(this DbContext dbcontext, Expression<Func<TEntity, bool>> exp = null) where TEntity : class
        {
            var dbSet = dbcontext.Set<TEntity>().AsQueryable();
            if (exp != null)
                dbSet = dbSet.Where(exp);
            return dbSet.Count();
        }

        /// <summary>
        /// 更新
        /// </summary>
        /// <typeparam name="TEntity">实体对象类型</typeparam>
        /// <param name="dbcontext">数据库上下文</param>
        /// <param name="entity">实体对象</param>
        /// <returns></returns>
        public static int Update<TEntity>(this DbContext dbcontext, TEntity entity) where TEntity : class
        {
            dbcontext.Set<TEntity>().Attach(entity);
            PropertyInfo[] props = entity.GetType().GetProperties();
            foreach (PropertyInfo prop in props)
            {
                if (prop.GetValue(entity, null) != null)
                {
                    if (prop.GetValue(entity, null).ToString() == "&nbsp;")
                        dbcontext.Entry(entity).Property(prop.Name).CurrentValue = null;
                    dbcontext.Entry(entity).Property(prop.Name).IsModified = true;
                }
            }
            return dbcontext.SaveChanges();
        }

        

        /// <summary>
        /// 按指定id更新实体,会更新整个实体
        /// </summary>
        /// <typeparam name="TEntity">实体对象类型</typeparam>
        /// <param name="dbcontext">数据库上下文</param>
        /// <param name="identityExp">Id筛选条件</param>
        /// <param name="entity">实体对象</param>
        public static void Update<TEntity>(this DbContext dbcontext, Expression<Func<TEntity, object>> identityExp, TEntity entity) where TEntity : class
        {
            dbcontext.Set<TEntity>().AddOrUpdate(identityExp, entity);
            dbcontext.Save<TEntity>();
        }
        
        /// <summary>
        /// 保存修改
        /// </summary>
        /// <typeparam name="TEntity">实体对象类型</typeparam>
        /// <param name="dbcontext">数据库上下文</param>
        public static void Save<TEntity>(this DbContext dbcontext) where TEntity : class
        {
            try
            {
                dbcontext.SaveChanges();
            }
            catch (DbEntityValidationException e)
            {
                throw new Exception(e.EntityValidationErrors.First().ValidationErrors.First().ErrorMessage);
            }
        }

        /// <summary>
        /// 按需要部分更新
        /// <para>如：Update(u =>u.Id==1,u =>new User{Name="ok"});</para>
        /// </summary>
        /// <typeparam name="TEntity">实体对象类型</typeparam>
        /// <param name="dbcontext">数据库上下文</param>
        /// <param name="where">筛选条件where</param>
        /// <param name="exp"></param>
        public static void Update<TEntity>(this DbContext dbcontext, Expression<Func<TEntity, bool>> where, Expression<Func<TEntity, TEntity>> entity) where TEntity : class
        {
            dbcontext.Set<TEntity>().Where(where).Update(entity);
        }

        /// <summary>
        /// 删除
        /// </summary>
        /// <typeparam name="TEntity">实体对象类型</typeparam>
        /// <param name="dbcontext">数据库上下文</param>
        /// <param name="entity">实体对象</param>
        /// <returns></returns>
        public static int Delete<TEntity>(this DbContext dbcontext, TEntity entity) where TEntity : class
        {
            dbcontext.Set<TEntity>().Attach(entity);
            dbcontext.Entry<TEntity>(entity).State = EntityState.Deleted;
            return dbcontext.SaveChanges();
        }

        /// <summary>
        /// 删除
        /// </summary>
        /// <typeparam name="TEntity">实体对象类型</typeparam>
        /// <param name="dbcontext">数据库上下文</param>
        /// <param name="exp">筛选条件</param>
        /// <returns></returns>
        public static int Delete<TEntity>(this DbContext dbcontext, Expression<Func<TEntity, bool>> exp) where TEntity : class
        {
            var entitys = dbcontext.Set<TEntity>().Where(exp).ToList();
            entitys.ForEach(m => dbcontext.Entry<TEntity>(m).State = EntityState.Deleted);
            return dbcontext.SaveChanges();
        }

        /// <summary>
        /// 根据主键查找
        /// </summary>
        /// <typeparam name="TEntity">实体对象类型</typeparam>
        /// <param name="dbcontext">数据库上下文</param>
        /// <param name="keyValue">主键的值</param>
        /// <returns></returns>
        public static TEntity FindEntity<TEntity>(this DbContext dbcontext, object keyValue) where TEntity : class
        {
            return dbcontext.Set<TEntity>().Find(keyValue);
        }

        /// <summary>
        /// 根据筛选条件查找
        /// </summary>
        /// <typeparam name="TEntity">实体对象类型</typeparam>
        /// <param name="dbcontext">数据库上下文</param>
        /// <param name="exp">筛选条件</param>
        /// <returns>找到的第一个实体或者默认值</returns>
        public static TEntity FindEntity<TEntity>(this DbContext dbcontext, Expression<Func<TEntity, bool>> exp) where TEntity : class
        {
            return dbcontext.Set<TEntity>().FirstOrDefault(exp);
        }

        /// <summary>
        /// 以AsNoTracking方式返回结果集
        /// 以AsNoTracking方式的详细说明：
        /// Gets a table with "no tracking" enabled (EF feature) Use it only when you load record(s) only for read-only operations
        /// 只用来显示 而不用保存实体 查询出来数据后直接更改数据会报错
        /// EF先把数据集缓存到EF Context，然后再返回客户端，以AsQueryable方式返回应该不缓存数据集
        /// 对于只读操作，强烈建议使用AsNoTracking进行数据获取，这样省去了访问EF Context的时间，会大大降低数据获取的时间。
        /// </summary>
        /// <typeparam name="TEntity">实体对象类型</typeparam>
        /// <param name="dbcontext">数据库上下文</param>
        /// <returns></returns>
        public static IQueryable<TEntity> IQueryable<TEntity>(this DbContext dbcontext) where TEntity : class
        {
            return dbcontext.Set<TEntity>().AsQueryable().AsNoTracking();
        }

        /// <summary>
        /// 以AsNoTracking方式返回结果集
        /// 以AsNoTracking方式的详细说明：
        /// Gets a table with "no tracking" enabled (EF feature) Use it only when you load record(s) only for read-only operations
        /// 只用来显示 而不用保存实体 查询出来数据后直接更改数据会报错
        /// EF先把数据集缓存到EF Context，然后再返回客户端，以AsQueryable方式返回应该不缓存数据集
        /// 对于只读操作，强烈建议使用AsNoTracking进行数据获取，这样省去了访问EF Context的时间，会大大降低数据获取的时间。
        /// </summary>
        /// <typeparam name="TEntity">实体对象类型</typeparam>
        /// <param name="dbcontext">数据库上下文</param>
        /// <param name="exp">筛选条件</param>
        /// <returns></returns>
        public static IQueryable<TEntity> IQueryable<TEntity>(this DbContext dbcontext, Expression<Func<TEntity, bool>> exp) where TEntity : class
        {
            return dbcontext.Set<TEntity>().AsQueryable().AsNoTracking().Where(exp);
        }

        /// <summary>
        /// 执行SQL语句
        /// 例如：
        ///   string sqlTextCount = String.Format("SELECT COUNT(1) AS CT  FROM ({0}) t", sqlText);
        ///   FindList(sqlTextCount)
        /// </summary>
        /// <typeparam name="TEntity">实体对象类型</typeparam>
        /// <param name="dbcontext">数据库上下文</param>
        /// <param name="strSql">SQL语句</param>
        /// <returns></returns>
        public static List<TEntity> FindList<TEntity>(this DbContext dbcontext, string strSql) where TEntity : class
        {
            return dbcontext.Database.SqlQuery<TEntity>(strSql).ToList<TEntity>();
        }

        /// <summary>
        /// 执行SQL语句
        /// </summary>
        /// <typeparam name="TEntity">实体对象类型</typeparam>
        /// <param name="dbcontext">数据库上下文</param>
        /// <param name="strSql">SQL语句</param>
        /// <param name="dbParameter">DbParameter[]</param>
        /// <returns></returns>
        public static List<TEntity> FindList<TEntity>(this DbContext dbcontext, string strSql, DbParameter[] dbParameter) where TEntity : class
        {
            return dbcontext.Database.SqlQuery<TEntity>(strSql, dbParameter).ToList<TEntity>();
        }
    }
}
